[小ネタ] S3 SelectでCSVの列名に特殊文字が入っている場合
問題
S3 Selectで、以下のようにCSVの列名に特殊な文字が入っている場合の話です。
aaa/aaa,bbb?bbb,ccc;ccc aaa1,bbb1,ccc1 aaa2,bbb2,ccc2
ここで例えばaaa/aaa
列だけを取り出したいとき、どうすれば良いでしょうか?
とりあえずやってみる
Python (boto3)でやってみます。まず、以下のようなコードを書いてみます。FileHeaderInfo: USE
としてヘッダ行を使用する設定にしています。
#!/usr/bin/env python # -*- coding: utf-8 -*- import boto3 client = boto3.client('s3', 'ap-northeast-1') bucket_name = 'test-bucket-name' key = 'test.csv' response = client.select_object_content( Bucket = bucket_name, Key = key, ExpressionType = 'SQL', Expression = 'SELECT s.aaa/aaa FROM S3Object s', InputSerialization = { 'CompressionType': 'NONE', 'CSV': { 'FileHeaderInfo': 'USE', 'RecordDelimiter': '\n', 'FieldDelimiter': ',' } }, OutputSerialization = { 'CSV': { 'RecordDelimiter': '\n', 'FieldDelimiter': ',' } } ) for event in response[ 'Payload' ]: if 'Records' in event: records = event[ 'Records' ][ 'Payload' ].decode( 'utf-8' ) print(records)
これを実行すると、以下のようなエラーになります。
botocore.exceptions.ClientError: An error occurred (MissingHeaders) when calling the SelectObjectContent operation: Some headers in the query are missing from the file. Please check the file and try again.
では、どうすれば良いのでしょうか?
解決
前置きが長すぎましたが、列名aaa/aaa
をダブルクォートでくくれば解決します。分かっていれば簡単ですね。
Expression = 'SELECT s."aaa/aaa" FROM S3Object s',
実行すると以下のように正しく結果が得られます。
aaa1 aaa2
なお、S3 Selectにおいては、以下のようにダブルクォートには特別な意味があります。
- ダブルクォートでくくると、大文字・小文字が区別されるようになります ヘッダー/属性名の大文字と小文字の区別
- 予約語が列名になっているような場合もダブルクォートでくくれば使えます ユーザー定義の用語としての予約キーワードの使用
補足:スラッシュを含むCSV
そもそも列名にそんな変な文字入れなきゃいいじゃん、という話なのですが、実は列名にスラッシュが含まれているCSVが存在します。 それがAWSのコストと使用状況レポート(AWS Cost & Usage Report, 以下CUR)です。
CURのヘッダ行にはlineItem/ProductCode
, lineItem/UsageType
のようにスラッシュが含まれています。
ですので、これをS3 Selectで扱う場合はダブルクォートが必要です。例えばCURからS3関係の行だけを取り出したければ以下のようになります。
SELECT * FROM S3Object s WHERE s."lineItem/ProductCode" = 'AmazonS3'
業務上でCURを扱う機会があり、S3 Selectを使ってみたらちょっとハマったのでメモとして残してみました。レアケースだとは思いますが、どこかで誰かのお役に立てば幸いです。